package com.whz.governance.sensitive.engine;

import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 基于trie tree的敏感词搜索引擎,需要初始化后过滤文本
 *
 * @author whz
 */
@Slf4j
public class SensitiveKeyEngine {

  private static final int TRY_TIMES = 3;
  private final AtomicInteger version = new AtomicInteger(Integer.MIN_VALUE + 1);

  private final AtomicStampedReference<SkTrieTree> tree = new AtomicStampedReference<>(null,
      version.incrementAndGet());


  /**
   * 线程安全地构建敏感词trie树,自循环3次
   *
   * @param sensitiveWords 敏感词列表
   * @return true 更新成功,false 更新失败
   */
  public boolean buildSensitiveTree(final List<String> sensitiveWords) {
    if (CollUtil.isEmpty(sensitiveWords)) {
      return false;
    }
    // 构建trie树
    SkTrieTree trieTree = new SkTrieTree();
    trieTree.buildTrie(sensitiveWords);

    // CAS自循环次数
    int i = TRY_TIMES;

    // CAS
    boolean success = false;
    while (!(success = atomicUpdate(trieTree)) && i-- > 0) {
      if (log.isDebugEnabled()) {
        log.debug("CAS is trying , {} times.", i);
      }
    }
    return success;
  }

  public List<String> matchSensitiveWords(final String text) {
    return Optional.of(tree.getReference())
        .orElseThrow(() -> new RuntimeException("trie树尚未初始化."))
        .filter(text);
  }

  public boolean atomicUpdate(SkTrieTree newOne) {

    // CAS
    SkTrieTree oldOne = tree.getReference();
    int oldVersion = tree.getStamp();
    int newVersion = version.incrementAndGet();

    return tree.compareAndSet(oldOne, newOne, oldVersion, newVersion);
  }

}